  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <math.h>  
#include <sys/time.h>   
  
#include <xvid.h>
#include "encode.h"


/***************************************************************************** 
 *                            Quality presets 
 ****************************************************************************/  
  
const int motion_presets[] = {  
    /* quality 0 */  
    0,  
  
    /* quality 1 */  
    XVID_ME_ADVANCEDDIAMOND16,  
  
    /* quality 2 */  
    XVID_ME_ADVANCEDDIAMOND16 | XVID_ME_HALFPELREFINE16,  
  
    /* quality 3 */  
    XVID_ME_ADVANCEDDIAMOND16 | XVID_ME_HALFPELREFINE16 |  
    XVID_ME_ADVANCEDDIAMOND8 | XVID_ME_HALFPELREFINE8,  
  
    /* quality 4 */  
    XVID_ME_ADVANCEDDIAMOND16 | XVID_ME_HALFPELREFINE16 |  
    XVID_ME_ADVANCEDDIAMOND8 | XVID_ME_HALFPELREFINE8 |  
    XVID_ME_CHROMA_PVOP | XVID_ME_CHROMA_BVOP,  
  
    /* quality 5 */  
    XVID_ME_ADVANCEDDIAMOND16 | XVID_ME_HALFPELREFINE16 |  
    XVID_ME_ADVANCEDDIAMOND8 | XVID_ME_HALFPELREFINE8 |  
    XVID_ME_CHROMA_PVOP | XVID_ME_CHROMA_BVOP,  
  
    /* quality 6 */  
    XVID_ME_ADVANCEDDIAMOND16 | XVID_ME_HALFPELREFINE16 | XVID_ME_EXTSEARCH16 |  
    XVID_ME_ADVANCEDDIAMOND8 | XVID_ME_HALFPELREFINE8 | XVID_ME_EXTSEARCH8 |  
    XVID_ME_CHROMA_PVOP | XVID_ME_CHROMA_BVOP,  
  
};  

  
const int vop_presets[] = {  
    /* quality 0 */  
    0,  
  
    /* quality 1 */  
    0,  
  
    /* quality 2 */  
    XVID_VOP_HALFPEL,  
  
    /* quality 3 */  
    XVID_VOP_HALFPEL | XVID_VOP_INTER4V,  
  
    /* quality 4 */  
    XVID_VOP_HALFPEL | XVID_VOP_INTER4V,  
  
    /* quality 5 */  
    XVID_VOP_HALFPEL | XVID_VOP_INTER4V |  
    XVID_VOP_TRELLISQUANT,  
  
    /* quality 6 */  
    XVID_VOP_HALFPEL | XVID_VOP_INTER4V |  
    XVID_VOP_TRELLISQUANT | XVID_VOP_HQACPRED,  
  
};  


/***************************************************************************** 
 *                     Command line global variables 
 ****************************************************************************/  
   
  
static xvid_enc_zone_t ZONES[MAX_ZONES];  
static int NUM_ZONES = 0;  

static int ARG_BITRATE = 0;  
  
//static int ARG_MAXFRAMENR = ABS_MAXFRAMENR;  
static int ARG_MAXKEYINTERVAL = 0;  
//static char *ARG_INPUTFILE = NULL;    

static int ARG_BQRATIO = 150;  
static int ARG_BQOFFSET = 100;  
static int ARG_MAXBFRAMES = 0;  
static int ARG_PACKED = 0;  
static int ARG_DEBUG = 0;  
static int ARG_VOPDEBUG = 0;  
static int ARG_GREYSCALE = 0;  
static int ARG_QTYPE = 0;  
static int ARG_QMATRIX = 0;  
static int ARG_GMC = 0;  
static int ARG_INTERLACING = 0;  
static int ARG_QPEL = 0;  
static int ARG_TURBO = 0;  
static int ARG_VHQMODE = 0;  
static int ARG_BVHQ = 0;  
static int ARG_CLOSED_GOP = 0;  

static float ARG_FRAMERATE = 25.00f;

/**************************************************************************** 
 *                     Nasty global vars ;-) 
 ***************************************************************************/    
  
  
/* Internal structures (handles) for encoding and decoding */  
void *enc_handle = NULL;  
  
static unsigned char qmatrix_intra[64];  
static unsigned char qmatrix_inter[64];  
  

/***************************************************************************** 
 *                        "statistical" functions 
 * 
 *  these are not needed for encoding or decoding, but for measuring 
 *  time and quality, there in nothing specific to XviD in these 
 * 
 *****************************************************************************/  
  
/* Return time elapsed time in miliseconds since the program started */  
 double  
msecond()  
{  
    struct timeval tv;  
  
    gettimeofday(&tv, 0);  
    return (tv.tv_sec * 1.0e3 + tv.tv_usec * 1.0e-3);  
}  
  
/***************************************************************************** 
 *                             Usage message 
 *****************************************************************************/  
  
 void  
usage()  
{  
    fprintf(stderr, "Usage : xvid_stat [OPTIONS]\n\n");  
    fprintf(stderr, "Input options:\n");  
    fprintf(stderr, " -i      string : input filename (default=stdin)\n");  
    fprintf(stderr, " -type   integer: input data type (yuv=0, pgm=1)\n");  
    fprintf(stderr, " -w      integer: frame width ([1.2048])\n");  
    fprintf(stderr, " -h      integer: frame height ([1.2048])\n");  
    fprintf(stderr, " -frames integer: number of frames to encode\n");  
    fprintf(stderr, "\n");  
    fprintf(stderr, "Output options:\n");  
    fprintf(stderr, " -dump    : save decoder output\n");  
    fprintf(stderr, " -save    : save an Elementary Stream file per frame\n");  
    fprintf(stderr, " -o string: save an Elementary Stream for the complete sequence\n");  
    fprintf(stderr, "\n");  
    fprintf(stderr, "BFrames options:\n");  
    fprintf(stderr, " -max_bframes   integer: max bframes (default=0)\n");  
    fprintf(stderr, " -bquant_ratio  integer: bframe quantizer ratio (default=150)\n");  
    fprintf(stderr, " -bquant_offset integer: bframe quantizer offset (default=100)\n");  
    fprintf(stderr, "\n");  
    fprintf(stderr, "Rate control options:\n");  
    fprintf(stderr, " -framerate float               : target framerate (>0 | default=25.0)\n");  
    fprintf(stderr, " -bitrate   integer             : target bitrate\n");  
    fprintf(stderr, " -single                        : single pass mode\n");  
    fprintf(stderr, " -pass1     filename            : twopass mode (first pass)\n");  
    fprintf(stderr, " -pass2     filename            : twopass mode (2nd pass)\n");  
    fprintf(stderr, " -zq starting_frame float       : bitrate zone; quant\n");  
    fprintf(stderr, " -zw starting_frame float       : bitrate zone; weight\n");  
    fprintf(stderr, " -max_key_interval integer      : maximum keyframe interval\n");  
    fprintf(stderr, "\n");  
    fprintf(stderr, "Other options\n");  
    fprintf(stderr, " -noasm           : do not use assembly optmized code\n");  
    fprintf(stderr, " -turbo           : use turbo presets for higher encoding speed\n");  
    fprintf(stderr, " -quality integer : quality ([0..%d])\n", ME_ELEMENTS - 1);  
    fprintf(stderr, " -vhqmode integer : level of Rate-Distortion optimizations ([0..4]) (default=0)\n");  
    fprintf(stderr, " -bvhq            : use Rate-Distortion optimizations for B-frames too\n");  
    fprintf(stderr, " -qpel            : use quarter pixel ME\n");  
    fprintf(stderr, " -gmc             : use global motion compensation\n");  
    fprintf(stderr, " -qtype   integer : quantization type (H263:0, MPEG4:1) (default=0)\n");  
    fprintf(stderr, " -qmatrix filename: use custom MPEG4 quantization matrix\n");  
    fprintf(stderr, " -interlaced      : use interlaced encoding (this is NOT a deinterlacer!)\n");  
    fprintf(stderr, " -packed          : packed mode\n");  
    fprintf(stderr, " -closed_gop      : closed GOP mode\n");  
    fprintf(stderr, " -grey            : grey scale coding (chroma is discarded)\n");  
    fprintf(stderr, " -lumimasking     : use lumimasking algorithm\n");  
    fprintf(stderr, " -stats           : print stats about encoded frames\n");  
    fprintf(stderr, " -debug           : activates xvidcore internal debugging output\n");  
    fprintf(stderr, " -vop_debug       : print some info directly into encoded frames\n");  
    fprintf(stderr, " -help            : prints this help message\n");  
    fprintf(stderr, "\n");  
    fprintf(stderr, "NB: You can define %d zones repeating the -z[qw] option as many times as needed.\n", MAX_ZONES);  
    fprintf(stderr, "\n");  
}  
  
/* Initialize encoder for first use, pass all needed parameters to the codec */  
int  
enc_init(int use_assembler)  
{  
    int xerr;   
    xvid_plugin_single_t single;
    xvid_enc_plugin_t plugins[7];  
    xvid_gbl_init_t xvid_gbl_init;  
    xvid_enc_create_t xvid_enc_create;  
  
    /*------------------------------------------------------------------------ 
     * XviD core initialization 
     *----------------------------------------------------------------------*/  
  
    /* Set version -- version checking will done by xvidcore */  
    memset(&xvid_gbl_init, 0, sizeof(xvid_gbl_init));  
    xvid_gbl_init.version = XVID_VERSION;  
    xvid_gbl_init.debug = ARG_DEBUG;  
  
  
    /* Do we have to enable ASM optimizations ? */  
    if (use_assembler) {  
  
#ifdef ARCH_IS_IA64  
        xvid_gbl_init.cpu_flags = XVID_CPU_FORCE | XVID_CPU_ASM;  
#else  
        xvid_gbl_init.cpu_flags = 0;  
#endif  
    } else {  
        xvid_gbl_init.cpu_flags = XVID_CPU_FORCE;  
    }  
  
    /* Initialize XviD core -- Should be done once per __process__ */  
    xvid_global(NULL, XVID_GBL_INIT, &xvid_gbl_init, NULL);  
  
    /*------------------------------------------------------------------------ 
     * XviD encoder initialization 
     *----------------------------------------------------------------------*/  
  
    /* Version again */  
    memset(&xvid_enc_create, 0, sizeof(xvid_enc_create));  
    xvid_enc_create.version = XVID_VERSION;  
  
    /* Width and Height of input frames */  
    xvid_enc_create.width = XDIM;  
    xvid_enc_create.height = YDIM;  
    xvid_enc_create.profile = XVID_PROFILE_AS_L4;  
  
    /* init plugins  */  
    xvid_enc_create.zones = ZONES;  
    xvid_enc_create.num_zones = NUM_ZONES;  
  
    xvid_enc_create.plugins = plugins;  
    xvid_enc_create.num_plugins = 0;  
  
    if (ARG_SINGLE) {  
        memset(&single, 0, sizeof(xvid_plugin_single_t));  
        single.version = XVID_VERSION;  
        single.bitrate = ARG_BITRATE;  
  
        plugins[xvid_enc_create.num_plugins].func = xvid_plugin_single;  
        plugins[xvid_enc_create.num_plugins].param = &single;  
        xvid_enc_create.num_plugins++;  
    }  
  
    /* No fancy thread tests */  
    xvid_enc_create.num_threads = 0;  
  
    /* Frame rate - Do some quick float fps = fincr/fbase hack */  
    if ((ARG_FRAMERATE - (int) ARG_FRAMERATE) < SMALL_EPS) {  
        xvid_enc_create.fincr = 1;  
        xvid_enc_create.fbase = (int) ARG_FRAMERATE;  
    } else {  
        xvid_enc_create.fincr = FRAMERATE_INCR;  
        xvid_enc_create.fbase = (int) (FRAMERATE_INCR * ARG_FRAMERATE);  
    }  
  
    /* Maximum key frame interval */  
    if (ARG_MAXKEYINTERVAL > 0) {  
        xvid_enc_create.max_key_interval = ARG_MAXKEYINTERVAL;  
    }else {  
        xvid_enc_create.max_key_interval = (int) ARG_FRAMERATE *10;  
    }  
  
    /* Bframes settings */  
    xvid_enc_create.max_bframes = ARG_MAXBFRAMES;  
    xvid_enc_create.bquant_ratio = ARG_BQRATIO;  
    xvid_enc_create.bquant_offset = ARG_BQOFFSET;  
  
    /* Dropping ratio frame -- we don't need that */  
    xvid_enc_create.frame_drop_ratio = 0;  
  
    /* Global encoder options */  
    xvid_enc_create.global = 0;  
  
    if (ARG_PACKED)  
        xvid_enc_create.global |= XVID_GLOBAL_PACKED;  
  
    if (ARG_CLOSED_GOP)  
        xvid_enc_create.global |= XVID_GLOBAL_CLOSED_GOP;  
  
    if (ARG_STATS)  
        xvid_enc_create.global |= XVID_GLOBAL_EXTRASTATS_ENABLE;  
  
    /* I use a small value here, since will not encode whole movies, but short clips */  
    xerr = xvid_encore(NULL, XVID_ENC_CREATE, &xvid_enc_create, NULL);  
  
    /* Retrieve the encoder instance from the structure */  
    enc_handle = xvid_enc_create.handle;  
    
    return (xerr);  
}  
  
int  
enc_stop()  
{  
    int xerr;  
  
    /* Destroy the encoder instance */  
    xerr = xvid_encore(enc_handle, XVID_ENC_DESTROY, NULL, NULL);  
  
    return (xerr);  
}  
  
int  
enc_main(unsigned char *image,  
         unsigned char *bitstream,  
         int *key,  
         int *stats_type,  
         int *stats_quant,  
         int *stats_length,  
         int sse[3])  
{  
    int ret;  
  
    xvid_enc_frame_t xvid_enc_frame;  
    xvid_enc_stats_t xvid_enc_stats;  
  
    /* Version for the frame and the stats */  
    memset(&xvid_enc_frame, 0, sizeof(xvid_enc_frame));  
    xvid_enc_frame.version = XVID_VERSION;  
  
    memset(&xvid_enc_stats, 0, sizeof(xvid_enc_stats));  
    xvid_enc_stats.version = XVID_VERSION;  
  
    /* Bind output buffer */  
    xvid_enc_frame.bitstream = bitstream;  
    xvid_enc_frame.length = -1;  

    if (image) {  
      
        xvid_enc_frame.input.plane[0] = image;  
	xvid_enc_frame.input.csp = XVID_CSP_YUY2;  
        xvid_enc_frame.input.stride[0] = XDIM*2;    
    } else {  
        xvid_enc_frame.input.csp = XVID_CSP_NULL;  
    }
  
  /* Set up core's general features */  
    xvid_enc_frame.vol_flags = 0;  
    if (ARG_STATS)  
        xvid_enc_frame.vol_flags |= XVID_VOL_EXTRASTATS;  
    if (ARG_QTYPE)  
        xvid_enc_frame.vol_flags |= XVID_VOL_MPEGQUANT;  
    if (ARG_QPEL)  
        xvid_enc_frame.vol_flags |= XVID_VOL_QUARTERPEL;  
    if (ARG_GMC)  
        xvid_enc_frame.vol_flags |= XVID_VOL_GMC;  
    if (ARG_INTERLACING)  
        xvid_enc_frame.vol_flags |= XVID_VOL_INTERLACING;  
  
    /* Set up core's general features */  
    xvid_enc_frame.vop_flags = vop_presets[ARG_QUALITY];  
  
    if (ARG_VOPDEBUG) {  
        xvid_enc_frame.vop_flags |= XVID_VOP_DEBUG;  
    }  
  
    if (ARG_GREYSCALE) {  
        xvid_enc_frame.vop_flags |= XVID_VOP_GREYSCALE;  
    }  
  
    /* Frame type -- let core decide for us */  
    xvid_enc_frame.type = XVID_TYPE_AUTO;  
  
    /* Force the right quantizer -- It is internally managed by RC plugins */  
    xvid_enc_frame.quant = 0;  
  
    /* Set up motion estimation flags */  
    xvid_enc_frame.motion = motion_presets[ARG_QUALITY];  
  
    if (ARG_GMC)  
        xvid_enc_frame.motion |= XVID_ME_GME_REFINE;  
  
    if (ARG_QPEL)  
        xvid_enc_frame.motion |= XVID_ME_QUARTERPELREFINE16;  
    if (ARG_QPEL && (xvid_enc_frame.vop_flags & XVID_VOP_INTER4V))  
        xvid_enc_frame.motion |= XVID_ME_QUARTERPELREFINE8;  
  
    if (ARG_TURBO)  
        xvid_enc_frame.motion |= XVID_ME_FASTREFINE16 | XVID_ME_FASTREFINE8 |   
                                 XVID_ME_SKIP_DELTASEARCH | XVID_ME_FAST_MODEINTERPOLATE |   
                                 XVID_ME_BFRAME_EARLYSTOP;  
  
    if (ARG_BVHQ)   
        xvid_enc_frame.vop_flags |= XVID_VOP_RD_BVOP;  
  
    switch (ARG_VHQMODE) /* this is the same code as for vfw */  
    {  
    case 1: /* VHQ_MODE_DECISION */  
        xvid_enc_frame.vop_flags |= XVID_VOP_MODEDECISION_RD;  
        break;  
  
    case 2: /* VHQ_LIMITED_SEARCH */  
        xvid_enc_frame.vop_flags |= XVID_VOP_MODEDECISION_RD;  
        xvid_enc_frame.motion |= XVID_ME_HALFPELREFINE16_RD;  
        xvid_enc_frame.motion |= XVID_ME_QUARTERPELREFINE16_RD;  
        break;  
  
    case 3: /* VHQ_MEDIUM_SEARCH */  
        xvid_enc_frame.vop_flags |= XVID_VOP_MODEDECISION_RD;  
        xvid_enc_frame.motion |= XVID_ME_HALFPELREFINE16_RD;  
        xvid_enc_frame.motion |= XVID_ME_HALFPELREFINE8_RD;  
        xvid_enc_frame.motion |= XVID_ME_QUARTERPELREFINE16_RD;  
        xvid_enc_frame.motion |= XVID_ME_QUARTERPELREFINE8_RD;  
        xvid_enc_frame.motion |= XVID_ME_CHECKPREDICTION_RD;  
        break;  
  
    case 4: /* VHQ_WIDE_SEARCH */  
        xvid_enc_frame.vop_flags |= XVID_VOP_MODEDECISION_RD;  
        xvid_enc_frame.motion |= XVID_ME_HALFPELREFINE16_RD;  
        xvid_enc_frame.motion |= XVID_ME_HALFPELREFINE8_RD;  
        xvid_enc_frame.motion |= XVID_ME_QUARTERPELREFINE16_RD;  
        xvid_enc_frame.motion |= XVID_ME_QUARTERPELREFINE8_RD;  
        xvid_enc_frame.motion |= XVID_ME_CHECKPREDICTION_RD;  
        xvid_enc_frame.motion |= XVID_ME_EXTSEARCH_RD;  
        break;  
  
    default :  
        break;  
    }  
      
    if (ARG_QMATRIX) {  
        /* We don't use special matrices */  
        xvid_enc_frame.quant_intra_matrix = qmatrix_intra;  
        xvid_enc_frame.quant_inter_matrix = qmatrix_inter;  
    }  
    else {  
        /* We don't use special matrices */  
        xvid_enc_frame.quant_intra_matrix = NULL;  
        xvid_enc_frame.quant_inter_matrix = NULL;  
    }  
  
    /* Encode the frame */  
    ret = xvid_encore(enc_handle, XVID_ENC_ENCODE, &xvid_enc_frame,  
                      &xvid_enc_stats);  
  
    *key = (xvid_enc_frame.out_flags & XVID_KEYFRAME);  
    *stats_type = xvid_enc_stats.type;  
    *stats_quant = xvid_enc_stats.quant;  
    *stats_length = xvid_enc_stats.length;  
    sse[0] = xvid_enc_stats.sse_y;  
    sse[1] = xvid_enc_stats.sse_u;  
    sse[2] = xvid_enc_stats.sse_v;  
  
    return (ret);  
}